
/**
 * 异步参数对象
 * @property max 最大并发数
 * @property retry 失败重试次数
 * @property endCallback 队列结束回调函数
 */
export type AsyncParams = {
  max?: number;
  retry?: number;
  endCallback?: () => void;
};

export type RunFun = (...args: any) => Promise<any>;

/**
 * 异步任务对象
 */
export interface RunFunBoj <T extends RunFun = ()=>Promise<any>>{
  status: "wait" | "ing" | "end" | "fail";
  target: T;
  retry: number;
  callback?: <D>(data:D) => void;
  err?: (e:any) => void;
}
/**
 * 异步队列
 */
export class asyncQueue {
  #Queue: RunFunBoj[] = [];
  #runNum = 0;
  #retry = 0;
  /**
   * 最大运行时 请求数量
   */
  #maxNum = 3;
  /**
   * 当前队列的运行状况
   */
  #status: "ing" | "end" = "end";
  #endCallback: () => void;
  constructor({
    max,
    retry,
    endCallback,
  }: AsyncParams) {
    if (max) {
      this.#maxNum = max;
    }
    if (retry) {
      this.#retry = retry;
    }

    this.#endCallback = endCallback || function () { };
  }

  /**
   * 添加任务
   * @param f 目标函数
   * @param callback 成功回调函数
   * @param err 错误回调函数
   * @returns 添加成功与否
   */
  push<T extends RunFun>(f: T, callback?: <D>(data:D) => void, err?: (e:any) => void) {
    if (f.constructor.name !== "Function") {
      return false;
    }
    // 队列对象 运行时
    const funObj: RunFunBoj = {
      status: "wait",
      target: f,
      retry: this.#retry,
      callback,
      err,
    };
    this.#Queue.push(funObj);
    this.clear();
    return true;
  }

  /**
   * 清除运行完成的任务
   */
  clear(fial = false) {
    this.#Queue = this.#Queue.filter((item) =>
      item.status !== "end" && fial ? item.status !== "fail" : true,
    );
  }

  /**
   * 添加并运行任务 若是没有传入函数 运行 之前传入的任务
   * @param f 目标函数
   * @param callback 成功回调函数
   * @param err 错误回调函数
   * @returns 添加成功与否
   */
  async run<T extends RunFun>(f?: T, callback?: <D>(data:D) => void, err?: (e:any) => void) {
    if (f) {
      // 注入函数
      this.push(f, callback, err);
    }
    if (this.#runNum >= this.#maxNum) {
      return;
    }
    let fun: RunFunBoj | null = null;
    for (const f of this.#Queue) {
      if (f.status == "wait") {
        fun = f;
        break;
      }
    }
    if (fun) {
      let data: any = null;
      try {
        this.#status = "ing";
        fun.status = "ing";
        this.#runNum++;
        data = await fun.target();
        if (fun.callback instanceof Function) {
          fun.callback(data);
        }
        fun.status = "end";
      } catch (e) {
        if (fun.err instanceof Function) {
          if (fun.retry > 0) {
            fun.status = "wait";
            fun.retry--;
          } else {
            fun.err(e);
            fun.status = "fail";
          }
        }
      } finally {
        this.#runNum--;
        this.run();
      }
    } else {
      for (const f of this.#Queue) {
        if (f.status != "end" && f.status != "fail") {
          return;
        }
      }
      this.#status = "end";
      this.#endCallback();
    }
  }
  get status() {
    return this.#status;
  }
  /**
   * 获取所有的 任务
   * @returns
   */
  getTasks() {
    return this.#Queue;
  }

  /**
   * 异步运行并得到返回值
   * @param fun 目标函数
   * @returns
   */
  async wait<T extends RunFun>(fun: T ){
    return await new Promise<Awaited <ReturnType<typeof fun>>>((rej, rea) => {
      this.run(fun, rej as any, rea);
    });
  }
}

const mergePromise = new WeakMap();

/**
 * 在某个异步请求 参数相同 返回的值相同
 * 并且在同一时间段 会发送多个亲求的场景
 * 压缩正在进行的异步请求 为一个
 * 避免同时条用多个请求导致后续请求 挂起
 * @param asyncFun 异步函数
 * @param param 函数传参 必须传入支持序列化的
 * @returns
 */
export async function MergeAsync<T extends RunFun>(
  asyncFun: T,
  ...param: any
): Promise<ReturnType<T>> {
  const key = JSON.stringify(param) as unknown as object;
  if (mergePromise.has(key)) {
    return mergePromise.get(key) as ReturnType<T>;
  }
  mergePromise.set(key, asyncFun(...param));
  const r = (await asyncFun(...param)) as ReturnType<T>;
  mergePromise.delete(key);
  return r;
}
